package org.anachronos.jeep.agent.instrumentation;

import java.lang.reflect.Method;
import java.util.Map;

import org.anachronos.jeep.core.trace.TraceModelBuilder;
import org.ow2.util.asm.MethodVisitor;
import org.ow2.util.asm.Type;
import org.ow2.util.asm.commons.AdviceAdapter;


class StatementAdviceAdapter extends AdviceAdapter {
	private static final String JDBC_TRACING_CONSTANTS_CLAZZ = Type.getInternalName(JdbcTracingConstants.class);
	private static final String STRING_CLAZZ = Type.getInternalName(String.class);
	private static final String MAP_CLAZZ = Type.getInternalName(Map.class);
	private static final String MAP_CLAZZ_DESC = Type.getDescriptor(Map.class);
	
	private static final String TRACE_MODEL_BUILDER_CLAZZ = 
		Type.getInternalName(TraceModelBuilder.class);
	
	private static final Method EXECUTE_STATEMENT_METHOD;
	private static final String EXECUTE_STATEMENT_METHOD_DESC;
	
	private static final Method FINISH_STATEMENT_METHOD;
	private static final String FINISH_STATEMENT_METHOD_DESC;
	
	private static final Method GET_METHOD;
	private static final String GET_METHOD_DESC;
	
	static {
		try {
			EXECUTE_STATEMENT_METHOD = TraceModelBuilder.class.getDeclaredMethod("executeStatement", String.class, String.class);
			EXECUTE_STATEMENT_METHOD_DESC = Type.getMethodDescriptor(EXECUTE_STATEMENT_METHOD);
			
			FINISH_STATEMENT_METHOD = TraceModelBuilder.class.getDeclaredMethod("finishStatement");
			FINISH_STATEMENT_METHOD_DESC = Type.getMethodDescriptor(FINISH_STATEMENT_METHOD);
			
			GET_METHOD = Map.class.getDeclaredMethod("get", Object.class);
			GET_METHOD_DESC = Type.getMethodDescriptor(GET_METHOD);
		} catch (Exception e) {
			throw new IllegalStateException(e);
		}
	}

	private final String className;
	private final boolean isPreparedStatement;

	public StatementAdviceAdapter(final MethodVisitor mv, final int access, final String name,
			final String desc, final String className, final boolean isPreparedStatement) {
		super(mv, access, name, desc);
		this.className = className.replace("/", ".");
		this.isPreparedStatement = isPreparedStatement;
	}

	@Override
	protected void onMethodEnter() {
		mv.visitLdcInsn(className);
		if (isPreparedStatement) {
			mv.visitFieldInsn(GETSTATIC, JDBC_TRACING_CONSTANTS_CLAZZ, 
					"PREPARED_STATEMENT_MAP", MAP_CLAZZ_DESC);
			mv.visitVarInsn(ALOAD, 0); // load this
			mv.visitMethodInsn(INVOKEINTERFACE, MAP_CLAZZ, 
					GET_METHOD.getName(), GET_METHOD_DESC);
			mv.visitTypeInsn(CHECKCAST, STRING_CLAZZ);
		} else {
			mv.visitVarInsn(ALOAD, 1);
		}
		mv.visitMethodInsn(INVOKESTATIC, 
				TRACE_MODEL_BUILDER_CLAZZ, 
				EXECUTE_STATEMENT_METHOD.getName(), EXECUTE_STATEMENT_METHOD_DESC);
		mv.visitInsn(POP);
	}
	
	@Override
	protected void onMethodExit(int opcode) {
		mv.visitMethodInsn(INVOKESTATIC, 
				TRACE_MODEL_BUILDER_CLAZZ, 
				FINISH_STATEMENT_METHOD.getName(), FINISH_STATEMENT_METHOD_DESC);		
	}
}
